# -*- coding: utf-8 -*-
import atexit
import gc
import os
import re
import sys
import time
import traceback
import rust_stdf_helper
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QApplication, QFileDialog, QMessageBox, QAbstractItemView, QHeaderView
from PyQt5.QtCore import Qt, QStringListModel, pyqtSlot as Slot, QTranslator
from openpyxl.reader.excel import load_workbook
from deps.ui.MainWindow import MainWindowUI
from dataload.t3annlysis import DataAnalysis
from src.sharedSrc import tab, readConfigFile, getSetting, saveConfigFile
from dataload.chartData import *
from deps.MergePanel import MergePanel
from deps.uic_stdExporter import ExporterPanel
from deps.uic_stdFailMarker import FailMarker
from waferMap.cate import WaferMap as cateChart
from waferMap.bin import WaferMap as binChart
from waferMap.map import WaferMap as mapChart
from waferMap.binSummary import BinChart as binSummary
from waferMap.trend import TrendChart as trendChart
from waferMap.waferSummary import WaferSummary as waferSummary
from deps.ui.popupLoading import Loading
from deps.uic_stdSettings import Settings
from openpyxl import Workbook
from deps.wafercdf import CdfPanel

# save config path to sys
rootFolder = os.path.dirname(sys.argv[0])
setattr(sys, "rootFolder", rootFolder)
setattr(sys, "CONFIG_PATH", os.path.join(rootFolder, "config.ini"))
setattr(sys, "LANGUAGE_PATH", os.path.join(rootFolder, "src", "tran"))


class MyMainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super(MyMainWindow, self).__init__()
        # 读取配置文件
        self.waferSelectListModel = None
        readConfigFile()

        self.settings = getSetting()
        # deps
        self.mainUI = MainWindowUI()
        self.mainUI.setupUi(self)

        # 翻译
        self.summaryChartData = None
        self.translatorUI = QTranslator(self)  # QTranslator 是 Qt 框架中用于进行国际化和本地化的类。 它可以用于将 UI 上的文本从一种语言翻译成另一种语言。
        self.translatorCode = QTranslator(self)

        self.pass_bin = self.settings.pass_bin  # 设置bin通过的对象
        # self.changeFont()

        # set main deps
        self.binChartData = None
        self.cateChartData = None  # 表格数据
        self.test_selection_listModel = None
        self.testName = None
        self.signal = None
        self.nowWaferElectionList = None  # wafer 的值
        self.dataAnalysis = None  # 数据分析
        self.filePath = None  # 文件路径
        # self.conf = None  # 配置文件信息
        self.nowTestSelectionList = []  # 現在測試的testselect的值

        # sub windows  子窗口
        self.mergePanel = MergePanel(self)  # merge
        # export
        self.exportPanel = ExporterPanel(self)
        # failmarker
        self.failmarker = FailMarker(self)
        self.cdfPanel = CdfPanel(self)

        # settings
        self.settingUI = Settings(self)
        # loading
        self.loading = Loading()
        sys.excepthook = self.onException  # 总结来说，这段代码处理未捕获的异常，通过日志记录详细的异常信息，并且更新某个状态（可能是用户界面或内部状态），以反映发生了异常。
        # loadFonts()  # 加载字体
        self.changeLanguage()

        # 初始化 action信息
        self.initActions()
        # 设置char的初始值
        self.BinFlag = True
        self.CateFlag = True
        self.mapFlag = True
        self.trendFlag = True
        self.summaryFlag = True
        self.waferSummaryFlag = True

        self.changeFont()  # 改变字体

        atexit.register(self.onExit)

    def updateData(self) -> None:
        """更新数据"""
        start = time.time()
        self.loading.loading(6)
        # 保存更改后的文件路径
        self.settings.recent_folder = self.filePath
        self.mainUI.search_lineEdit.clear()
        # 加载数据  当文件加载错误时，就让文件路径为空
        self.loading.loading(value=1)
        try:
            self.dataAnalysis = DataAnalysis(self.filePath)
        except:
            self.filePath = None
        self.nowTestSelectionList = self.dataAnalysis.testSelectionList  # 選擇框的值
        self.loading.loading(value=2)
        # 增加数据 test select
        self.addTestSelectionData(self.nowTestSelectionList)
        # self.nowWaferElectionList = self.dataAnalysis.waferSelectionList  # wafer的值
        self.loading.loading(value=3)
        # 增加数据 wafer select
        self.addWaferSelection(self.dataAnalysis.testName)
        # 文件信息更新
        self.addFileInfo(self.dataAnalysis.metaDataList)
        self.loading.loading(value=4)
        # # 设置char的初始值  读取新文件后进行更新
        self.BinFlag = True
        self.CateFlag = True
        self.summaryFlag = True
        self.mapFlag = True
        self.trendFlag = True
        self.waferSummaryFlag = True

        if self.filePath:
            # 当加载新文件的时候，进行插入
            currentTab = self.mainUI.tw.currentIndex()
            self.clear_layout(hbin=True, sbin=True, map=True, trend=True, binSum=True, waferSum=True)
            if currentTab != tab.INFO:
                self.updateStatus(self.tr("Drawing map! Please wait..."), False, False, False)
            if currentTab == tab.BIN and self.BinFlag:
                self.mainUI.hbinChart.setWaferData(binChartData(self.dataAnalysis.df))
                self.BinFlag = False
            elif currentTab == tab.CATE and self.CateFlag:
                self.mainUI.sbinChart.setWaferData(cateChartData(self.dataAnalysis.df))
                self.CateFlag = False
            elif currentTab == tab.SUMMARY and self.summaryFlag:
                self.mainUI.binSummary.setBinData(bin_summary_data(self.dataAnalysis.df))
                self.CateFlag = False
        self.loading.loading(value=5)
        # 导入文件后可点击
        self.mainUI.tw.setTabEnabled(tab.BIN, True)
        self.mainUI.tw.setTabEnabled(tab.CATE, True)
        self.mainUI.tw.setTabEnabled(tab.SUMMARY, True)
        # 更新info page 数据
        self.updateTestDataTable()
        # settings
        self.settingUI.removeColorBtns()

        self.settingUI.initColorBtns(self.dataAnalysis.get_cate(),
                                     self.dataAnalysis.get_bin()[3])
        self.settingUI.splitColorBtn(self.dataAnalysis.get_measure_key(self.settings.wafer_split))
        self.settingUI.initSymbolBtns()
        self.loading.loading(value=6)
        self.loading.loading(close=True)
        end = time.time()
        self.updateStatus(self.tr("File loaded successfully, time elapsed %.2f sec.") % (end - start), False, False,
                          False)
        # 改变加载进来的字体
        self.changeFont()

    def changeFont(self):
        _app = QApplication.instance()
        font_id = QtGui.QFontDatabase.addApplicationFont(self.settings.font_path)
        if font_id == -1:
            self.updateStatus(self.tr("Failed to load the font!"))
        else:
            font_family = QtGui.QFontDatabase.applicationFontFamilies(font_id)[0]
            newfont = QtGui.QFont(font_family)
            _app.setFont(newfont)
            _ = [w.setFont(newfont) if not isinstance(w, QtWidgets.QListView) else None for w in
                 QApplication.allWidgets()]
            # actions is not listed in qapp all widgets, iterate separately
            _ = [w.setFont(newfont) for w in self.mainUI.toolBar.actions()]

    def changeLanguage(self):
        _app = QApplication.instance()
        if self.settings.language == "English" or self.settings.language == "英文":
            tran = b''
            self.translatorUI.loadFromData(tran)
            self.translatorCode.loadFromData(tran)
            self.failmarker.translatorUI.loadFromData(tran)
            self.failmarker.translator.loadFromData(tran)
            self.exportPanel.translatorUI.loadFromData(tran)
            self.exportPanel.translatorCode.loadFromData(tran)
            self.mergePanel.translatorCode.loadFromData(tran)
            self.mergePanel.translatorUI.loadFromData(tran)
            self.settingUI.translatorUI.loadFromData(tran)
        elif self.settings.language == "简体中文":
            self.translatorUI.load(os.path.join(sys.LANGUAGE_PATH, "zh_mainWindow.qm"))
            self.translatorCode.load(os.path.join(sys.LANGUAGE_PATH, 'zh_t3main.qm'))
            self.failmarker.translatorUI.load(os.path.join(sys.LANGUAGE_PATH, 'zh_loading.qm'))
            self.failmarker.translator.load(os.path.join(sys.LANGUAGE_PATH, 'zh_failmaker.qm'))
            self.exportPanel.translatorUI.load(os.path.join(sys.LANGUAGE_PATH, 'zh_export.qm'))
            self.exportPanel.translatorCode.load(os.path.join(sys.LANGUAGE_PATH, 'zh_exportcode.qm'))
            self.mergePanel.translatorCode.load(os.path.join(sys.LANGUAGE_PATH, 'zh_mergecode.qm'))
            self.mergePanel.translatorUI.load(os.path.join(sys.LANGUAGE_PATH, 'zh_merge.qm'))
            self.settingUI.translatorUI.load(os.path.join(sys.LANGUAGE_PATH, 'zh_settings.qm'))

        _app.installTranslator(self.translatorUI)
        self.mainUI.retranslateUi()
        _app.installTranslator(self.translatorCode)
        # exporter
        _app.installTranslator(self.exportPanel.translatorUI)
        self.exportPanel.exportUI.retranslateUi(self.exportPanel)
        # fail maker loading
        _app.installTranslator(self.failmarker.translatorUI)
        self.failmarker.UI.retranslateUi(self.failmarker)
        _app.installTranslator(self.failmarker.translator)
        # exporterCode
        _app.installTranslator(self.exportPanel.translatorCode)
        # merge
        _app.installTranslator(self.mergePanel.translatorCode)
        _app.installTranslator(self.mergePanel.translatorUI)
        self.mergePanel.mergeUI.retranslateUi(self.mergePanel)
        # setting
        _app.installTranslator(self.settingUI.translatorUI)
        self.settingUI.settingsUI.retranslateUi(self.settingUI)

    def onMerge(self) -> None:
        # 实现合并txtToCs
        """
        增加merge的界面
        """
        self.mergePanel.showUI()

    def onExport(self) -> None:
        """增加导出文件的界面"""
        if self.filePath is not None:
            self.exportPanel.showUI()
        else:
            # no data is found, show a warning dialog
            QMessageBox.warning(self, self.tr("Warning"), self.tr("No file is loaded."))

    def onSettings(self):
        self.settingUI.showUI()

    def initActions(self) -> None:
        """捕获 action 信息 """
        # 捕获打开文件的点击事件
        self.mainUI.actionOpen.triggered.connect(self.openFile)
        # 捕获 merge事件
        self.mainUI.actionMerge.triggered.connect(self.onMerge)
        # 捕获 fail marker 事件
        self.mainUI.actionFailMarker.triggered.connect(self.onFailMarker)
        # 捕获 onExport 事件
        self.mainUI.actionExport.triggered.connect(self.onExport)
        # 捕获打开文件的点击事件
        self.mainUI.actionAbout.triggered.connect(self.showAbout)
        # 捕获settings点击事件
        self.mainUI.actionSettings.triggered.connect(self.onSettings)

        # 清理搜索框按钮
        self.mainUI.clearButton.clicked.connect(self.clearSearchBox)

        # 搜索框文本改变
        self.mainUI.search_lineEdit.textChanged.connect(self.searchSelect)

        # # 晶體選擇框
        self.mainUI.wafer_selection_listView.clicked.connect(self.waferSelect)
        #
        # 获得当前的testName signal
        self.mainUI.test_selection_listView.clicked.connect(self.getTestNameSignal)

        # # 增加点击test selection 框的元素显示状态
        self.mainUI.test_selection_listView.clicked.connect(self.testSelectionToStates)

        # update chart index
        self.mainUI.tw.currentChanged.connect(self.chartSelect)
        # 未导入文件不可点击chart
        self.mainUI.tw.setTabEnabled(tab.BIN, False)
        self.mainUI.tw.setTabEnabled(tab.SUMMARY, False)
        self.mainUI.tw.setTabEnabled(tab.CATE, False)
        self.mainUI.tw.setTabEnabled(tab.MAP, False)
        self.mainUI.tw.setTabEnabled(tab.TREND, False)
        self.mainUI.tw.setTabEnabled(tab.WAFER_SUMMARY, False)

        # export
        # 接收信号，连接到自定义槽函数
        self.exportPanel.MapSignal.connect(self.savePlot)
        self.exportPanel.loadData.connect(self.exportLoadData)
        self.failmarker.statusSignal.connect(self.updateStatus)
        self.settingUI.settingSignal.connect(self.settingInfo)
        self.mergePanel.loadDataSignal.connect(self.mergeUpdate)
        self.mainUI.mapChart.cdfSignal.connect(self.cdfUpdate)

        # 使窗口可以接收拖拽事件
        self.setAcceptDrops(True)
        self.installEventFilter(self)

        # info page
        self.mainUI.infoBox.currentChanged.connect(self.updateTestDataTable)

    def getTestNameSignal(self, index) -> None:
        """获得 testname 和 signal"""
        # ata(self, index: QModelIndex, role: int)
        self.testName, self.signal = self.test_selection_listModel.data(index, 0).split()
        # 点击选择 testname 和 signal 才可以进行
        self.mainUI.tw.setTabEnabled(tab.MAP, True)
        self.mainUI.tw.setTabEnabled(tab.TREND, True)
        self.mainUI.tw.setTabEnabled(tab.WAFER_SUMMARY, True)

        # 清空刷新的数据
        self.clear_layout(map=True, trend=True, waferSum=True)
        # 如果当前处于 map chart 时候更新对象
        currentTab = self.mainUI.tw.currentIndex()

        self.mapFlag = True
        self.trendFlag = True
        self.waferSummaryFlag = True

        if currentTab == tab.MAP:
            self.updateStatus(self.tr("Drawing map! Please wait..."), False, False, False)
            ma = self.settings.wafer_units[self.settings.wafer_select_A]
            mv = self.settings.wafer_units[self.settings.wafer_select_V + 4]
            self.mainUI.mapChart.setWaferData(
                mapSplitData(self.dataAnalysis.df, self.testName, self.signal,
                             self.settings.wafer_precision,
                             self.settings.wafer_split, ma, mv))
            self.mapFlag = False
            self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)
        elif currentTab == tab.TREND:
            self.updateStatus(self.tr("Drawing map! Please wait..."), False, False, False)

            loadData = self.mainUI.trendChart.setTrendData(trend_data(self.dataAnalysis.df, self.testName, self.signal))
            if not loadData:
                self.updateStatus(self.tr('The current TestName and signal results do not contain voltage or current '
                                          'data. Please select a new one.'), True, False, False)
            else:
                self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)
            self.trendFlag = False
        elif currentTab == tab.WAFER_SUMMARY:
            loadData = get_waferSummary(self.dataAnalysis.df, self.testName, self.signal,
                                        self.settings.wafer_split)
            if not loadData:
                self.updateStatus(self.tr('There is no voltage or current, or a mixture of voltage and current, '
                                          'please select again.'), True, False, False)
            else:
                self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)
                self.mainUI.waferSummary.setSummaryData(loadData)
                self.waferSummaryFlag = False
        gc.collect()

    def openFile(self) -> None:
        """打开文件的点击事件"""
        self.updateStatus(self.tr("File is loading."), False, False, False)
        try:
            file, _ = QFileDialog.getOpenFileNames(self, caption=self.tr("Select File To Open"),
                                                   directory=self.settings.recent_folder,
                                                   filter=self.settings.file_filter)
        except:
            file, _ = QFileDialog.getOpenFileNames(self, caption=self.tr("Select File To Open"))
        file_length = len(file)
        if file_length == 1:
            self.updateStatus(self.tr("File loaded successfully!"), False, False, False, msecss=1000)
            self.filePath = file[0]
            # 加载数据
            self.updateData()
        else:
            if not file_length:
                self.updateStatus(self.tr("File not loaded!"), False, False, False, msecss=1000)
                # self.updateStatus("File not loaded!", warning=True)
            else:
                self.updateStatus(self.tr("Please load a single file for processing!"), warning=True)

    def showAbout(self) -> None:
        # self.showStatus("正在打开关于信息！", 1000)
        """打开关于界面"""
        msgBox = QMessageBox(self)
        msgBox.setWindowTitle(self.tr("About"))
        # 设置富文本格式 富文本格式允许你使用HTML标签来格式化文本，例如改变字体、颜色等。
        # https://blog.csdn.net/qq_21438461/article/details/132928080
        msgBox.setTextFormat(Qt.TextFormat.RichText)
        msgBox.setText("<span style='color:#930DF2;font-size:20px'>CDASystem</span>"
                       "<br>{0}: {1}"
                       "<br>{2}: zj"
                       "<br>{3}: 2976245573@qq.com <br>".format(
            self.tr("Version"),
            "V1.1.0",
            self.tr("Author"),
            self.tr("Email")))
        msgBox.setInformativeText("{0}:<br>"
                                  "<a href='https://gitee.com/guo-jing-123/cdasystem/blob/master/README.md'>Tutorial file README</a>"
                                  "<br>"
                                  "<br>{1}".format(
            self.tr("For instructions, please refer to the ReadMe in the repo"), self.tr(
                "Description: This software is a completely open source software, suitable for processing chip information data analysis.")))
        msgBox.exec_()

    def onExit(self):
        saveConfigFile()
        # 删除所有子对象
        self.deleteLater()

    def clear_layout(self, hbin=False, sbin=False, map=False, trend=False, binSum=False, waferSum=False, cdf=False):
        # 清理图标数据
        if hbin:
            self.mainUI.hbinChart.plotlayout.clear()
        if sbin:
            self.mainUI.sbinChart.plotlayout.clear()
        if map:
            self.mainUI.mapChart.plotlayout.clear()
        if trend:
            self.mainUI.trendChart.plotlayout.clear()
        if binSum:
            self.mainUI.binSummary.plotlayout.clear()
        if waferSum:
            self.mainUI.waferSummary.plotlayout.clear()
        if cdf:
            self.cdfPanel.cdf_summary.plotlayout.clear()
        gc.collect()

    def chartSelect(self) -> None:
        """画图现在图像"""
        currentTab = self.mainUI.tw.currentIndex()
        if currentTab in [tab.CATE, tab.BIN, tab.SUMMARY]:
            self.mainUI.test_selection_listView.setDisabled(True)
            self.mainUI.search_lineEdit.setDisabled(True)
            self.mainUI.clearButton.setDisabled(True)
            self.mainUI.wafer_selection_listView.setDisabled(True)
        else:
            self.mainUI.test_selection_listView.setDisabled(False)
            self.mainUI.search_lineEdit.setDisabled(False)
            self.mainUI.clearButton.setDisabled(False)
            self.mainUI.wafer_selection_listView.setDisabled(False)
        if self.filePath:
            self.updateStatus(self.tr("Drawing map! Please wait..."), False, False, False)
            if currentTab == tab.BIN and self.BinFlag:
                self.binChartData = binChartData(self.dataAnalysis.df)
                self.mainUI.hbinChart.setWaferData(self.binChartData)
                self.BinFlag = False
            elif currentTab == tab.CATE and self.CateFlag:
                self.cateChartData = cateChartData(self.dataAnalysis.df)
                self.mainUI.sbinChart.setWaferData(self.cateChartData)
                self.CateFlag = False
            elif currentTab == tab.SUMMARY and self.summaryFlag:
                self.summaryChartData = bin_summary_data(self.dataAnalysis.df)
                self.mainUI.binSummary.setBinData(self.summaryChartData)
                self.summaryFlag = False
            elif currentTab == tab.INFO:  # 当切换到 data info的时候 数据依然更新
                self.mapFlag = True
                self.trendFlag = True
                self.waferSummaryFlag = True
                self.clear_layout(map=True, trend=True, waferSum=True)
                gc.collect()
            elif currentTab == tab.MAP and self.mapFlag:
                ma = self.settings.wafer_units[self.settings.wafer_select_A]
                mv = self.settings.wafer_units[self.settings.wafer_select_V + 4]
                self.mainUI.mapChart.setWaferData(
                    mapSplitData(self.dataAnalysis.df, self.testName, self.signal,
                                 self.settings.wafer_precision,
                                 self.settings.wafer_split, ma, mv))
                self.mapFlag = False
            elif currentTab == tab.WAFER_SUMMARY and self.waferSummaryFlag:
                loadData = get_waferSummary(self.dataAnalysis.df, self.testName, self.signal,
                                            self.settings.wafer_split)
                if not loadData:
                    self.updateStatus(self.tr('There is no voltage or current, or a mixture of voltage and current, '
                                              'please select again.'), True, False, False)
                else:
                    self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)
                    self.mainUI.waferSummary.setSummaryData(loadData)
                    self.waferSummaryFlag = False
            elif currentTab == tab.TREND and self.trendFlag:
                loadData = self.mainUI.trendChart.setTrendData(
                    trend_data(self.dataAnalysis.df, self.testName, self.signal))
                if not loadData:
                    self.updateStatus(
                        self.tr('The current TestName and signal results do not contain voltage or current '
                                'data. Please select a new one.'), True, False, False)
                self.trendFlag = False
            self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)

    def updateTestDataTable(self) -> None:
        """更新测试数据表"""
        if self.filePath is None:
            # do nothing if test data table is not selected
            return
        if self.mainUI.infoBox.currentIndex() == 1:
            self.test_summary()
        elif self.mainUI.infoBox.currentIndex() == 2:
            self.dut_summary()

    def test_summary(self) -> None:
        """加载结果集"""
        # 更新dut tests 数据
        stateDataList = self.dataAnalysis.separate_head()
        self.test_summary_model = QStandardItemModel()
        # model.setVerticalHeaderLabels(header)
        horizontalHeader = self.mainUI.rawDataTable.horizontalHeader()
        self.mainUI.rawDataTable.setModel(self.test_summary_model)

        for tmpRow in stateDataList:
            qitemRow = [QtGui.QStandardItem(ele if i == 0 else ele) for i, ele in enumerate(tmpRow)]
            self.test_summary_model.appendRow(qitemRow)
        for column in range(0, horizontalHeader.count()):
            horizontalHeader.setSectionResizeMode(column, QHeaderView.ResizeMode.ResizeToContents)

    def dut_summary(self) -> None:
        """加载dut summary数据"""
        if self.get_file_size_in_mb(self.filePath) > 10:
            self.updateStatus(
                self.tr("If the imported file is larger than 10MB, processing the file will affect the software "
                        "performance. You are advised to view the source file."), info=True)
            return
        self.loading.loading(4)
        # 更新dut tests 数据
        model = QStandardItemModel()
        model.setHorizontalHeaderLabels(self.dataAnalysis.df.columns)
        horizontalHeader = self.mainUI.dutInfoTable.horizontalHeader()
        self.mainUI.dutInfoTable.setModel(model)
        self.loading.loading(value=1)
        stateDataList = self.dataAnalysis.separate_information()
        self.loading.loading(value=2)
        for tmpRow in stateDataList:
            qitemRow = [QtGui.QStandardItem(ele if i == 0 else ele) for i, ele in enumerate(tmpRow)]
            model.appendRow(qitemRow)
        self.loading.loading(value=3)
        for column in range(0, horizontalHeader.count()):
            horizontalHeader.setSectionResizeMode(column, QHeaderView.ResizeMode.ResizeToContents)
        self.loading.loading(value=4)
        # Close the progress dialog when done
        self.loading.loading(close=True)

    # 数据导入设置
    def addFileInfo(self, fileInfoData) -> None:
        """
        文件信息展示
        :return:
        """
        # 建立一个数据表
        tmodel_info = QtGui.QStandardItemModel()
        self.mainUI.fileInfoTable.setModel(tmodel_info)
        self.mainUI.fileInfoTable.setTextElideMode(Qt.TextElideMode.ElideNone)
        self.mainUI.fileInfoTable.setSelectionMode(QAbstractItemView.SelectionMode.NoSelection)
        #设置滑动
        self.mainUI.fileInfoTable.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
        self.mainUI.fileInfoTable.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)

        # 清理之前的数据
        tmodel_info.removeRows(0, tmodel_info.rowCount())

        # 更新file info数据
        horizontalHeader = self.mainUI.fileInfoTable.horizontalHeader()
        verticalHeader = self.mainUI.fileInfoTable.verticalHeader()
        # 不显示 index
        horizontalHeader.setVisible(False)
        verticalHeader.setVisible(False)

        for tmpRow in fileInfoData:
            qitemRow = [QtGui.QStandardItem(ele if i == 0 else ele) for i, ele in enumerate(tmpRow)]
            tmodel_info.appendRow(qitemRow)
        # 调整行列的宽带和高度
        for column in range(0, horizontalHeader.count()):
            horizontalHeader.setSectionResizeMode(column, QHeaderView.ResizeMode.ResizeToContents)

        # resize to content to show all texts, then add additional height to each row
        for row in range(tmodel_info.rowCount()):
            verticalHeader.setSectionResizeMode(row, QHeaderView.ResizeMode.ResizeToContents)
            newHeight = verticalHeader.sectionSize(row) + 20
            verticalHeader.setSectionResizeMode(row, QHeaderView.ResizeMode.Fixed)
            verticalHeader.resizeSection(row, newHeight)

    def addTestSelectionData(self, data: list) -> None:
        """
        增加test selection 数据
        :return:
        """
        # QListView用于展示数据 https://www.cnblogs.com/ygzhaof/p/10077192.html
        self.test_selection_listModel = QStringListModel()
        self.test_selection_listModel.setStringList(data)
        self.mainUI.test_selection_listView.setModel(self.test_selection_listModel)

    def addWaferSelection(self, data: list) -> None:
        """选择芯片进行比较"""
        self.waferSelectListModel = QStringListModel()
        self.waferSelectListModel.setStringList(data)
        self.mainUI.wafer_selection_listView.setModel(self.waferSelectListModel)

    def testSelectionToStates(self) -> None:
        """增加点击test selection 框的元素显示状态到状态栏"""
        # 更新file info数据
        model = QStandardItemModel()
        model.setHorizontalHeaderLabels(['TestName', 'Signal', 'NO', 'Site', 'LowLimit', 'HighLimit', 'Force'])
        horizontalHeader = self.mainUI.testStatistics_TableView.horizontalHeader()
        self.mainUI.testStatistics_TableView.setModel(model)

        stateDataList = [self.dataAnalysis.testStatisticsData(self.testName, self.signal)]

        for tmpRow in stateDataList:
            qitemRow = [QtGui.QStandardItem(ele if i == 0 else ele) for i, ele in enumerate(tmpRow)]
            model.appendRow(qitemRow)
        for column in range(0, horizontalHeader.count()):
            horizontalHeader.setSectionResizeMode(column, QHeaderView.ResizeMode.ResizeToContents)

    def waferSelect(self, index) -> None:
        """wafer選擇框 QListView用于展示数据 https://www.cnblogs.com/ygzhaof/p/10077192.html"""
        # self.mainUI.tw.setTabEnabled(tab.WAFER_SUMMARY, True)
        testName = self.waferSelectListModel.data(index, 0)
        self.mainUI.search_lineEdit.clear()
        if self.mainUI.tw.currentIndex() == tab.WAFER_SUMMARY and testName != 'ALL':
            self.loading.loading(4)
            self.clear_layout(waferSum=True)
            self.loading.loading(value=1)
            loadData = get_waferSummary(self.dataAnalysis.df, testName,
                                        split=self.settings.wafer_split)
            self.loading.loading(value=2)
            if not loadData:
                self.loading.loading(value=3)
                self.loading.loading(value=4)
                self.updateStatus(self.tr('There is no voltage or current, or a mixture of voltage and current, '
                                          'please select again.'), True, False, False)
            else:
                self.loading.loading(value=3)
                self.mainUI.waferSummary.setSummaryData(loadData)
                self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)
                self.loading.loading(value=4)
            self.loading.loading(close=True)
        self.searchSelect(testName)
        self.addTestSelectionData(self.nowTestSelectionList)

    def clearSearchBox(self) -> None:
        """清理搜索框"""
        self.mainUI.search_lineEdit.clear()

    def searchSelect(self, testName: str = None) -> None:
        """搜索选择实现"""
        if testName:
            SString = testName
        else:
            SString = self.mainUI.search_lineEdit.text()
        if SString and SString != 'ALL':
            try:
                cpl_pattern = re.compile(SString, re.IGNORECASE)
                self.nowTestSelectionList = [item for item in self.dataAnalysis.testSelectionList if
                                             cpl_pattern.search(item)]
            except re.error:
                pass
        else:
            self.nowTestSelectionList = self.dataAnalysis.testSelectionList
            # if the string is empty, skip filter process
        self.addTestSelectionData(self.nowTestSelectionList)

    # status
    def onException(self, errorType, errorValue, tb) -> None:
        # logger.error("Uncaught Error occurred", exc_info=(errorType, errorValue, tb))
        errMsg = traceback.format_exception(errorType, errorValue, tb, limit=0)
        self.updateStatus("\n".join(errMsg), False, False, True)

    def onFailMarker(self) -> None:
        if self.filePath is not None:
            if self.mainUI.infoBox.currentIndex() == 1 and self.mainUI.tw.currentIndex() == tab.INFO:
                self.failmarker.start()
        else:
            self.updateStatus("No file is loaded.", warning=True)

    def updateStatus(self, new_msg: str, info: bool = False, warning: bool = False, error: bool = False,
                     msecss: int = None) -> None:
        if msecss:
            self.statusBar().showMessage(new_msg, msecss)
        else:
            self.statusBar().showMessage(new_msg)
        if info:
            QMessageBox.information(self, self.tr("Info"), new_msg)
        elif warning:
            QMessageBox.warning(self, self.tr("Warning"), new_msg)
            # logger.warning(new_msg)
        elif error:
            QMessageBox.critical(self, self.tr("Error"), new_msg)
            # sys.exit()
        QApplication.processEvents()

    # 获取文件大小的函数
    def get_file_size_in_mb(self, file_path: str) -> float:
        size_in_bytes = os.path.getsize(file_path)  # 获取文件大小（字节）
        size_in_mb = size_in_bytes / (1024 * 1024)  # 将字节转换为MB
        return size_in_mb

    # export
    @Slot(str, list, object, list)
    def savePlot(self, outPath: str, selectedContents: list, ReportSelection, exportTestItems: list) -> None:
        """保存所有类型的图和信息"""
        # Assuming total is defined elsewhere
        total = len(exportTestItems)
        self.loading.loading(total + 2)
        dirnamePath = os.path.dirname(outPath)  # 获取输出路径的目录
        fileName = os.path.basename(outPath).split('.')[0]
        saveImgPath = os.path.join(dirnamePath, fileName + "img")  # 拼接保存图片的路径
        self.exportMessage = ''  # 初始化导出信息
        plot_path_message = False
        if len(selectedContents) > 1 or total:
            plot_path_message = True
        if not os.path.exists(saveImgPath):
            os.makedirs(saveImgPath)
        self.loading.loading(value=1)
        # 保存 bin cate 图 和 info 信息
        for i in selectedContents:
            if i == ReportSelection.FileInfo:
                try:
                    book = load_workbook(outPath)
                except FileNotFoundError:
                    book = Workbook()
                testInfo = self.dataAnalysis.metaDataList
                sheet = book.active
                for i in range(len(testInfo)):
                    sheet.cell(row=i + 1, column=1, value=testInfo[i][0])
                    sheet.cell(row=i + 1, column=2, value=testInfo[i][1])
            elif i == ReportSelection.Bin:
                filename = os.path.join(saveImgPath, "bin.jpg")
                binChart().setWaferData(binChartData(self.dataAnalysis.df), filename)
            elif i == ReportSelection.Cate:
                filename = os.path.join(saveImgPath, "cate.jpg")
                cateChart().setWaferData(cateChartData(self.dataAnalysis.df), filename)
            elif i == ReportSelection.Summary:
                filename = os.path.join(saveImgPath, "bin_summary.jpg")
                binSummary().setBinData(bin_summary_data(self.dataAnalysis.df), filename)
            elif i == ReportSelection.Pkl:
                save_pkl = outPath.replace(".csv", ".pkl")
                self.dataAnalysis.df.to_pickle(save_pkl)
                self.exportMessage += self.tr(f"Pkl data: %s") % save_pkl + "\n"

        self.loading.loading(value=2)
        remove_testName = {}
        for i in range(total):
            sta = self.loading.loading(value=i + 3)
            if sta is False:
                break
            testName, signal = exportTestItems[i].split()
            if ReportSelection.Wafer in selectedContents:
                filename = os.path.join(saveImgPath, f"{testName}_{signal}_map.jpg")
                ma = self.settings.wafer_units[self.settings.wafer_select_A]
                mv = self.settings.wafer_units[self.settings.wafer_select_V + 4]
                loadData = mapSplitData(self.dataAnalysis.df, testName, signal,
                                        self.settings.wafer_precision,
                                        self.settings.wafer_split, ma, mv)
                mapChart().setWaferData(loadData, file_name=filename)
            if ReportSelection.FileInfo in selectedContents:
                sheet_name = self.sanitize_sheet_name(exportTestItems[i])  # 指定要使用的工作表名称
                if sheet_name in book.sheetnames:
                    sheet = book[sheet_name]  # 切换到指定的工作表
                else:
                    sheet = book.create_sheet(sheet_name)  # 创建一个新的工作表
                loadData = mapSplitData(self.dataAnalysis.df, testName, signal, {})
                all_xs = []
                all_ys = []
                all_vs = {}
                # Iterate through the keys and update xs, ys, and vs
                for key, data in loadData['Data'].items():
                    xs = [x + 1 for x in data['x']]  # Adjust row indices
                    ys = [y + 1 for y in data['y']]  # Adjust column indices
                    vs = data['measure']
                    all_xs.extend(xs)
                    all_ys.extend(ys)
                    # Update the sheet with the adjusted values
                    for x, y in zip(xs, ys):
                        key_name = f"{x - 1} {y - 1}"
                        cell_value = vs.get(key_name)  # Adjusted key also corresponds to x-1, y-1
                        if cell_value is not None:
                            sheet.cell(row=x, column=y, value=cell_value)
                            all_vs[key_name] = cell_value
                # Find the maximum y value to adjust the columns
                max_ys = max(all_ys) + 2
                # Update the sheet with additional data
                for i, (x, y) in enumerate(zip(all_xs, all_ys)):
                    key_name = f"{x - 1} {y - 1}"
                    cell_value = all_vs.get(key_name)
                    if cell_value is not None:
                        sheet.cell(row=i + 1, column=max_ys, value=x - 1)
                        sheet.cell(row=i + 1, column=max_ys + 1, value=y - 1)
                        u_v = self.value_and_unit(cell_value)
                        if u_v:
                            if (u_v['unit'] == '%' or u_v['unit'] == '%') and float(u_v['value']) == 0:
                                sheet.cell(row=i + 1, column=max_ys + 2, value='0')
                            else:
                                sheet.cell(row=i + 1, column=max_ys + 2, value=u_v['value'])
                            sheet.cell(row=i + 1, column=max_ys + 3, value=u_v['unit'])
                        else:
                            sheet.cell(row=i + 1, column=max_ys + 2, value=cell_value)
            if ReportSelection.Trend in selectedContents:
                filename = os.path.join(saveImgPath, f"{testName}_{signal}_trend.jpg")
                trendChart().setTrendData(trend_data(self.dataAnalysis.df, testName, signal), file_name=filename)
            if ReportSelection.Wafer_Summary in selectedContents:
                if testName not in remove_testName:
                    filename = os.path.join(saveImgPath, f"{testName}_waferSummary.jpg")
                    waferSummary().setSummaryData(get_waferSummary(self.dataAnalysis.df, testName,
                                                                   split=self.settings.wafer_split), filename=filename)
                    remove_testName[testName] = signal
                filename = os.path.join(saveImgPath, f"{testName}_{signal}_waferSummary.jpg")
                waferSummary().setSummaryData(get_waferSummary(self.dataAnalysis.df, self.testName, self.signal,
                                                               self.settings.wafer_split), filename=filename)
        if ReportSelection.FileInfo in selectedContents:
            # 保存文件
            book.save(outPath)
        # Close the progress dialog when done
        self.loading.loading(close=True)
        # 如果有生成图片，添加图片路径信息
        if plot_path_message:
            self.exportMessage += self.tr(f"Image data: %s") % saveImgPath
        # 最后弹出成功信息
        self.updateStatus(self.exportMessage, info=True)

    @Slot()
    def exportLoadData(self) -> None:
        """加载导入数据 testName signal"""
        self.exportPanel.refreshUI(self.dataAnalysis.testSelectionList)

    def value_and_unit(self, input_string):
        # 正则表达式匹配必须有一个数值和一个单位
        regex = r'^([+-]?\d*\.?\d+)\s*(mV|mA|uA|nA|A|uV|nV|V|%)$'
        match = re.match(regex, input_string)

        if match:
            # 第一个括号匹配的是数值部分
            value = match.group(1)
            # 第二个括号匹配的是单位部分
            unit = match.group(2)

            return {"value": value, "unit": unit}
        else:
            # 如果输入格式不匹配，则返回None或其他适当的错误处理
            return None

    def sanitize_sheet_name(self, name):
        # 移除不允许的字符
        invalid_chars = ':\/?*[]'
        for char in invalid_chars:
            name = name.replace(char, '')
        # 限制工作表名称长度不超过31个字符
        return name[:31]

    @Slot()
    def settingInfo(self):
        self.changeLanguage()
        self.changeFont()
        self.pass_bin = self.settings.pass_bin  # 设置bin通过的对象
        self.mainUI.trendChart.inin_Data()

        # 更新 map
        currentTab = self.mainUI.tw.currentIndex()
        if self.filePath:
            # 更新分割 按钮
            self.settingUI.splitColorBtn(self.dataAnalysis.get_measure_key(self.settings.wafer_split))

            if self.mainUI.infoBox.currentIndex() == 1:
                self.test_summary()
            # 更新 info page
            self.dataAnalysis.metaDataList = self.dataAnalysis.check_bin()
            self.addFileInfo(self.dataAnalysis.metaDataList)
            # 清理图标数据
            self.clear_layout(hbin=True, sbin=True, map=True, trend=True, binSum=True, waferSum=True)
            # 清理图标数据
            self.CateFlag = True
            self.BinFlag = True
            self.summaryFlag = True
            self.trendFlag = True
            self.mapFlag = True
            self.waferSummaryFlag = True
            if currentTab == tab.BIN:
                self.mainUI.hbinChart.setWaferData(binChartData(self.dataAnalysis.df))
                self.BinFlag = False
            elif currentTab == tab.CATE:
                self.mainUI.sbinChart.setWaferData(cateChartData(self.dataAnalysis.df))
                self.CateFlag = False
            elif currentTab == tab.SUMMARY:
                self.mainUI.binSummary.setBinData(bin_summary_data(self.dataAnalysis.df))
                self.summaryFlag = False
            elif currentTab == tab.TREND:
                self.mainUI.trendChart.setTrendData(trend_data(self.dataAnalysis.df, self.testName, self.signal))
                self.trendFlag = False
            elif currentTab == tab.MAP:
                ma = self.settings.wafer_units[self.settings.wafer_select_A]
                mv = self.settings.wafer_units[self.settings.wafer_select_V + 4]
                self.mainUI.mapChart.setWaferData(
                    mapSplitData(self.dataAnalysis.df, self.testName, self.signal,
                                 self.settings.wafer_precision,
                                 self.settings.wafer_split, ma, mv))
                self.mapFlag = False
            elif currentTab == tab.WAFER_SUMMARY:
                loadData = get_waferSummary(self.dataAnalysis.df, self.testName, self.signal,
                                            self.settings.wafer_split)
                if not loadData:
                    self.updateStatus(self.tr('There is no voltage or current, or a mixture of voltage and current, '
                                              'please select again.'), True, False, False)
                else:
                    self.mainUI.waferSummary.setSummaryData(loadData)
                    self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)
                    self.waferSummaryFlag = False

    @Slot(str)
    def mergeUpdate(self, file_path):
        self.filePath = file_path
        self.updateData()

    @Slot(str, str, str)
    def cdfUpdate(self, testName, x, y):
        loadData = get_waferSummary(self.dataAnalysis.df, testName, x=x, y=y, split=self.settings.wafer_split)
        if not loadData:
            self.updateStatus(self.tr('There is no voltage or current, or a mixture of voltage and current, '
                                      'please select again.'), True, False, False)
        else:
            self.updateStatus(self.tr("The painting is finished!"), False, False, False, msecss=1000)

            self.clear_layout(cdf=True)
            self.cdfPanel.cdf_summary.setSummaryData(loadData)
            self.cdfPanel.showUI()

    def eventFilter(self, source, event):
        """使窗口可以接收拖拽事件"""
        if event.type() == QtCore.QEvent.DragEnter:
            # 如果拖拽的对象是文件，接受拖拽事件
            if event.mimeData().hasUrls():
                event.acceptProposedAction()
            return True
        elif event.type() == QtCore.QEvent.Drop:
            # 处理拖拽释放事件，提取文件路径
            if event.mimeData().hasUrls():
                file_paths = [url.toLocalFile() for url in event.mimeData().urls()]
                if len(file_paths) > 1:
                    self.updateStatus(self.tr("Please load a single file for processing!"), warning=True)
                    return False
                file_path = file_paths[0]
                # Check the file extension
                if not (file_path.endswith('.csv') or file_path.endswith('.pkl') or file_path.endswith('.txt')):
                    self.updateStatus(self.tr("Unsupported file type! Please load a CSV or PKL file."), warning=True)
                    return False

                self.updateStatus(self.tr("File loaded successfully!"), False, False, False, msecss=1000)
                self.filePath = file_path
                # 加载数据
                self.updateData()
                return True
        return super(MyMainWindow, self).eventFilter(source, event)


def run():
    # 创建一个QApplication对象
    app = QApplication([])
    # 创建主窗口对象
    window = MyMainWindow()

    # 显示主窗口
    window.show()

    # 设置应用程序的图标
    """
    原函数：
    convert2QIcon = lambda raw: \
    QtGui.QIcon(
        QtGui.QPixmap.fromImage(
            QtGui.QImage.fromData(raw, format = 'SVG')))

    def getIcon(name: str) -> QtGui.QIcon:
        global iconDict
        if name not in iconDict:
            iconDict[name] = convert2QIcon(rust_stdf_helper.get_icon_src(name))
        return iconDict[name]
    """
    app.setWindowIcon(
        QtGui.QIcon(QtGui.QPixmap.fromImage(QtGui.QImage.fromData(rust_stdf_helper.get_icon_src("APP"), format='SVG'))))

    # 设置应用的样式为'Fusion'  参考 https://blog.csdn.net/u014779536/article/details/111152257
    app.setStyle('Fusion')

    # 设置高DPI支持 这可以使应用程序在高 DPI 屏幕上看起来更清晰 参考 https://blog.csdn.net/qq_21438461/article/details/131760212
    app.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps)

    # 运行应用程序的主循环
    sys.exit(app.exec_())


if __name__ == '__main__':
    run()
